/*
 * type_helper.h
 *
 *  Created on: 2020年4月25日
 *      Author: guyadong
 */

#ifndef DTALK_TYPE_HELPER_H_
#define DTALK_TYPE_HELPER_H_
#include <string>
#include <memory>
#include <algorithm>
#include <vector>
#include <set>
#include <list>
#include <map>
#include <iterator>
#include <ctime>
#include <sstream>
#include <type_traits>
#include <uri_utils.hpp>
#include <json_utilits.hpp>
#include <tsl/ordered_map.h>
#include <tsl/ordered_set.h>
#include <date_utilits.h>
#include <assert_macros.h>
#include <chrono>
#include <iomanip>
namespace std {
	inline std::string to_string(const std::string &input)
	{
		return input;
	}
	inline std::string to_string(const char* input)
	{
		return input ? std::string(input) : std::string("");
	}
	/** 转为ISO8601 时间格式 */
	inline std::string to_string(const std::tm &input) {
		std::stringstream ss;
		ss << std::put_time(&input, "%FT%T%z");
		return ss.str();
	}
	inline std::string to_string(const uri &input) {
		return input.to_string();
	}
	inline std::string to_string(char input) {
		std::stringstream ss;
		ss << input;
		return ss.str();
	}
	/** uint8_t 类型输出为十六进制(HEX)字符串 */
	inline std::string to_string(uint8_t input) {
		std::stringstream ss;
		ss << std::hex << (int)input;
		return ss.str();
	}
	/** int8_t 类型输出为十进制(DEC)字符串 */
	inline std::string to_string(int8_t input) {
		std::stringstream ss;
		ss << (int)input;
		return ss.str();
	}
	_BASIC_JSON_TPL_DECLARATION_
	std::string to_string(const _BASIC_JSON_TPL_ &j)
	{
		return nlohmann::to_string(j);
	}
	/** 模板函数,std::pair输出 */
	template<typename K,typename V>
	std::string to_string(const std::pair<K, V>& input)
	{
		std::stringstream ss;
		ss << "\"" <<to_string(input.first) << "\"" << ":" << "\"" << to_string(input.second) << "\"";
		return ss.str();
	}
	/**
	 * @brief 迭代器内容输出为字符串
	 * @param first 起始迭代器
	 * @param last 终止迭代器
	 * @param separator 分隔符
	 * @param begin 输出起始符
	 * @param end 输出结束符
	 * @param prefix 输出元素前缀
	 * @param suffix 输出元素后缀
	 */
	template<typename InputIt, typename T = typename std::iterator_traits<InputIt>::value_type>
	std::string join_to_string(InputIt first, InputIt last,
		const std::string &separator = "",
		const std::string &begin = "",
		const std::string &end = "",
		const std::string &prefix = "",
		const std::string &suffix = "")
	{
		std::stringstream ss;
		int c = 0;
		ss << begin;
		std::for_each(first, last, [&](const T &t) {
			if (c++) {
				ss << separator;
			}
			std::string str = to_string(t);
			ss << prefix << to_string(t) << suffix;
		});
		ss << end;
		return ss.str();
	}
	/**
	 * @brief 容器内容输出为字符串
	 * @param c 容器对象
	 * @param separator 分隔符
	 * @param begin 输出起始符
	 * @param end 输出结束符
	 * @param prefix 输出元素前缀
	 * @param suffix 输出元素后缀
	 */
	template<typename C,typename InputIt = typename C::const_iterator>
	std::string join_to_string(const C & c,
		const std::string &separator = "",
		const std::string &begin = "",
		const std::string &end = "",
		const std::string &prefix = "",
		const std::string &suffix = "")
	{
		return join_to_string<InputIt>(c.begin(), c.end(), separator, begin, end, prefix, suffix);
	}
	
	template<typename T>
	std::string to_string(const std::set<T> &input)
	{
		return join_to_string(input.begin(), input.end(), ",", "{", "}");
	}
	template<typename T>
	std::string to_string(const std::list<T> &input)
	{
		return join_to_string(input.begin(), input.end(), ",", "{", "}");
	}
	template<typename T>
	std::string to_string(const std::vector<T> &input)
	{
		return join_to_string(input.begin(), input.end(), ",", "{", "}");
	}
	template<typename K, typename V>
	std::string to_string(const std::map<K, V>& input)
	{
		return join_to_string(input.begin(), input.end(), ",", "{", "}");
	}
	template<typename T>
	std::string to_string(const tsl::ordered_set<T> &input)
	{
		return join_to_string(input.begin(), input.end(), ",", "{", "}");
	}
	template<typename K, typename V>
	std::string to_string(const tsl::ordered_map<K, V>& input)
	{
		return join_to_string(input.begin(), input.end(), ",", "{", "}");
	}
	////////////////////////////////////////////////////////
	/** 实现对容器中所有元素的hash值迭代计算 */
	template<typename C, typename InputIt = typename C::const_iterator>
	struct container_hash {
		using result_type = size_t;
		using argument_type = C;
		/** 容器元素类型 */
		using E = typename std::iterator_traits<InputIt>::value_type;
		result_type operator()(const argument_type & s) const noexcept {
			result_type h = 0;
			std::for_each(s.begin(), s.end(), [&](const E& e) {
				h ^= hash<E>().operator()(e);
			});
			return h;
		}
	};

	/** 为std::set提供std::hash特例化实现 */
	template<typename T>
	struct hash<std::set<T>> : container_hash<std::set<T>> {};

	/** 为std::list提供std::hash特例化实现 */
	template<typename T>
	struct hash<std::list<T>> : container_hash<std::list<T>> {};

	/** 为std::vector提供std::hash特例化实现 */
	template<typename T>
	struct hash<std::vector<T>> : container_hash<std::vector<T>> {};

	/** 为std::tm提供std::hash特例化实现 */
	template<>
	struct hash<std::tm> {
		typedef std::tm argument_type;
		typedef std::size_t result_type;
		result_type operator()(argument_type const& _tm) const noexcept {
			auto t = _tm;
			time_t time = std::mktime(&t) * 1000;
			return (result_type)(time);
		}
	};
	/** 为std::pair提供std::hash特例化实现 */
	template<typename T1,typename T2>
	struct hash<std::pair<T1,T2>> {
		typedef std::pair<T1, T2> argument_type;
		typedef std::size_t result_type;
		result_type operator()(argument_type const& _p) const noexcept {
			auto h1 = hash<T1>().operator()(_p.first);
			auto h2 = hash<T2>().operator()(_p.second);
			return (result_type)(h1 + h2);
		}
	};	

	/////////////// JSON ///////////////
	
	template<typename T, _BASIC_JSON_TPL_PARAM_DECL_>
	void to_json(_BASIC_JSON_TPL_ &j, const std::shared_ptr<T> &value)
	{
		if (value)
		{
			j = *value;
		}
		else
		{
			j = _BASIC_JSON_TPL_();
		}
	}
	template<typename T, _BASIC_JSON_TPL_PARAM_DECL_>
	void from_json(const _BASIC_JSON_TPL_ &j, std::shared_ptr<T> &value)
	{
		if (j.is_null())
		{
			value = nullptr;
		}
		else
		{
			value = std::make_shared<j.get<T>()>;
		}
	}
	namespace chrono {
		_BASIC_JSON_TPL_DECLARATION_
		void to_json(_BASIC_JSON_TPL_ & j, const time_point<system_clock, milliseconds>& value)
		{
			j = date::format_iso8601(value);
		}
		_BASIC_JSON_TPL_DECLARATION_
		void from_json(const _BASIC_JSON_TPL_ & j, time_point<system_clock, milliseconds>& value)
		{
			throw_if_msg(!j.is_string(), "JSON String type required")
				auto s = j.get<std::string>();
			auto ok = date::parse_uni_datetime(s, value);
			throw_if_msg(!ok, std::string("UNABLE parse date string: ") + s)
		}

		/** 转为ISO8601 时间格式 */

		inline std::string to_string(const std::time_t & input)
		{
			auto _tm = *std::localtime(&input);
			return std::to_string(_tm);
		}

	}
	_BASIC_JSON_TPL_DECLARATION_
	void to_json(_BASIC_JSON_TPL_ &j, const std::time_t &value)
	{
		auto _sys_clock = std::chrono::system_clock::from_time_t(value);
		auto _mills = std::chrono::time_point_cast<std::chrono::milliseconds>(_sys_clock);
		chrono::to_json(j, _mills);
	}
	_BASIC_JSON_TPL_DECLARATION_
	void from_json(const _BASIC_JSON_TPL_ &j, std::time_t &value)
	{
		std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds> _mills;
		chrono::from_json(j, _mills);
		value = std::chrono::system_clock::to_time_t(_mills);
	}
	_BASIC_JSON_TPL_DECLARATION_
	void to_json(_BASIC_JSON_TPL_ &j, const std::tm &value)
	{
		std::time_t _t = mktime((std::tm*)&value);
		to_json(j, _t);
	}
	_BASIC_JSON_TPL_DECLARATION_
	void from_json(const _BASIC_JSON_TPL_ &j, std::tm &value)
	{
		std::time_t _t;
		from_json(j, _t);
		value = *localtime(&_t);
	}

}/** namespace std */

inline bool operator==(const std::tm &t1, const std::tm & t2)
{
	return 0 == std::memcmp(&t1, &t2, sizeof(t1));
}

inline bool operator==(const uri &u1, const uri & u2)
{
	return u1.to_string() == u2.to_string();
}

namespace nlohmann {
	// uri 没有默认构造函数，要采用下面的方式提供序列化和反序列化方法,否则json::get<uri>()不能调用
	template <>
	struct adl_serializer<uri> {
		template<template<typename, typename, typename...> class ObjectType>
		static uri from_json(const basic_json<ObjectType>& j) {
			return{ j.template get<std::string>() };
		}
		template<template<typename, typename, typename...> class ObjectType>
		static void to_json(basic_json<ObjectType>& j, const uri &u) {
			j = std::to_string(u);
		}
	};
}
#endif /* DTALK_TYPE_HELPER_H_ */
